#include <prism/frontend/semantic_analysis/semantic_analysis.hpp>
//=====================================================================
#include <boost/bind.hpp>
//=====================================================================
#include <sooty/frontend/syntactic_analysis/algorithm.hpp>
//=====================================================================
#include <prism/frontend/syntactical_analysis/parse.hpp>
#include <prism/frontend/syntactical_analysis/marshall.hpp>
#include <prism/frontend/semantic_analysis/resolve/identifier.hpp>
#include <prism/frontend/semantic_analysis/resolve/typename.hpp>
#include <prism/frontend/semantic_analysis/resolve/primitive.hpp>
#include <prism/frontend/semantic_analysis/resolve/function.hpp>
//=====================================================================
using namespace prism;


//=====================================================================
//
//=====================================================================
struct primitive_type_adder
{
	void operator ()(sooty::parseme_ptr_ref N)
	{
		using namespace sooty;
		ATMA_ASSERT(N);
		ATMA_ASSERT(N->id == ID::root);
		N->children.push_back( parseme::create(N, ID::int_type, value_t(32), lexical_position(-1, -1, -1)) );
		N->children.push_back( parseme::create(N, ID::real_type, value_t(32), lexical_position(-1, -1, -1)) );
		N->children.push_back( parseme::create(N, ID::bool_type, sooty::value_t(), lexical_position(-1, -1, -1)) );
		
		parseme_ptr eq_ii = parseme::create(N, ID::eq_operator, value_t(), lexical_position());
		eq_ii->children.push_back( make_parseme(eq_ii, ID::bool_type, value_t()) );
		eq_ii->children.push_back( make_parseme(eq_ii, ID::int_type, value_t()) );
		eq_ii->children.push_back( make_parseme(eq_ii, ID::int_type, value_t()) );
		
		N->children.push_back(eq_ii);
	}
};



//=====================================================================
//
//=====================================================================
struct identifier_to_variable_definition_resolver
{
	void operator ()(sooty::parseme_ptr_ref N)
	{
		if (N->id == ID::identifier)
		{
			// we don't do this for the actual variable-definition itself,
			// and for the identifier of functions, nor for function-calls
			sooty::parseme_ptr parent = sooty::parseme_ptr(N->parent);
			if (parent->id != ID::variable_definition
			 && parent->id != ID::function
			 && parent->id != ID::function_call)
			{
				sooty::parseme_ptr P = resolve::identifier_to_variable_definition(N);
				ATMA_ASSERT(P);
				N->children.push_back(P);
			}
		}
	}
};


//=====================================================================
//
//=====================================================================
struct typename_to_type_definition_resolver
{
	void operator ()(sooty::parseme_ptr_ref N)
	{
		if (N->id == ID::variable_definition)
		{
			sooty::parseme_ptr_ref type = marshall::variable_definition::type(N);
			
			if (type->id == ID::int_type || type->id == ID::bool_type || type->id == ID::real_type)
			{
				sooty::parseme_ptr P = resolve::primitive_to_definition(type);
				ATMA_ASSERT(P);
				type = P;
			}
		}
		else if (N->id == ID::pointer_type)
		{
			if (N->children[0]->id == ID::int_type || N->children[0]->id == ID::bool_type)
			{
				sooty::parseme_ptr P = resolve::primitive_to_definition(N->children[0]);
				ATMA_ASSERT(P);
				N->children[0] = P;
			}
		}
		else if (N->id == ID::function)
		{
			sooty::parseme_ptr_ref type = marshall::function::return_type(N);
			
			if (type->id == ID::int_type || type->id == ID::bool_type)
			{
				sooty::parseme_ptr P = resolve::primitive_to_definition(type);
				ATMA_ASSERT(P);
				type = P;
			}
		}
		else if (N->id == ID::new_)
		{
			if (N->children[0]->id == ID::int_type || N->children[0]->id == ID::bool_type)
			{
				sooty::parseme_ptr P = resolve::primitive_to_definition(N->children[0]);
				ATMA_ASSERT(P);
				N->children[0] = P;
			}
		}
	}
};



//=====================================================================
// simplifies expression-trees (removes useless "add" and "mul" nodes
//=====================================================================

struct obsolete_expression_remover
{
	void operator ()(sooty::parseme_ptr_ref P)
	{
		for (sooty::parseme_container::iterator child_iter = P->children.begin(); child_iter != P->children.end(); ++child_iter)
		{
			sooty::parseme_ptr child = *child_iter;
			
			switch (child->id)
			{
				case ID::add:
				case ID::mul:
				{
					if (child->children.size() < 2) {
						*child_iter = child->children[0];
						(*child_iter)->parent = P;
					}
				}
			}
		}
	}
};


//=====================================================================
//
//=====================================================================
struct assign_type_to_int_literal
{
	void operator ()(sooty::parseme_ptr_ref P)
	{
		if (P->id == ID::int_literal) {
			if (P->children.empty())
				P->children.push_back( resolve::primitive_to_definition( sooty::parseme::create( P, ID::int_type, sooty::value_t(), sooty::lexical_position() ) ) );
		}
		else if (P->id == ID::bool_literal) {
			if (P->children.empty())
				P->children.push_back( resolve::primitive_to_definition( sooty::parseme::create( P, ID::bool_type, sooty::value_t(), sooty::lexical_position() ) ) );
		}
		else if (P->id == ID::real_literal) {
			if (P->children.empty())
				P->children.push_back( resolve::primitive_to_definition( sooty::parseme::create( P, ID::real_type, sooty::value_t(), sooty::lexical_position() ) ) );
		}
	}
};




//=====================================================================
//
//=====================================================================



void prism::semantic_analysis::module(sooty::parseme_ptr_ref N)
{
	primitive_type_adder()(N);
	sooty::depth_first_for_each(N, identifier_to_variable_definition_resolver(), false);
	sooty::depth_first_for_each(N, typename_to_type_definition_resolver(), false);
	sooty::depth_first_for_each(N, assign_type_to_int_literal());
	
	semantic_info info;
	
	// 1) first, sort by ID, so that we have all similar things together!
	std::sort(N->children.begin(), N->children.end(), sooty::ids_less_than());
	
	// 2) "do" all the functions
	std::pair<sooty::parseme_container::iterator, sooty::parseme_container::iterator> range = 
		std::equal_range(N->children.begin(), N->children.end(), prism::ID::function, sooty::ids_less_than());
	std::for_each(range.first, range.second, boost::bind(prism::semantic_analysis::function, boost::ref(info), _1));
}